package com.shangxia.monomer.study.designPatterns.creationType.singleton;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @ProjectName: monomer
 * @ClassName: SingletonDemo
 * @Time: 2021/7/18
 * @Author: zhouy
 * @Description: 单例模式
 * 推荐 枚举实现单例模式
 * DEMO:
 * java.lang.Runtime
 * java.util.Currency
 * org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
 * org.springframework.aop.framework.ProxyFactoryBean
 * @Experience:
 */

@Slf4j
public class SingletonDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
//        threadTest();
//        reflectionTest();
//        enumReflectionTest();
        serializableSingletonTest();
    }

    /**
     * 反序列化破坏单例
     */
    private static void serializableSingletonTest() throws IOException, ClassNotFoundException {
        InnerClassSingleton singleton = InnerClassSingleton.getInnerClassSingleton();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file/testSerializable"));
        oos.writeObject(singleton);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file/testSerializable"));
        InnerClassSingleton o = (InnerClassSingleton) ois.readObject();
        ois.close();
        System.out.println("单例1：" + (o == singleton));

        InnerClassSingletonSerializable singletonSerializable = InnerClassSingletonSerializable.getInnerClassSingleton();
        oos = new ObjectOutputStream(new FileOutputStream("file/testSerializable2"));
        oos.writeObject(singletonSerializable);
        oos.close();
        ois = new ObjectInputStream(new FileInputStream("file/testSerializable2"));
        InnerClassSingletonSerializable os = (InnerClassSingletonSerializable) ois.readObject();
        ois.close();
        System.out.println("单例2：" + (os == singletonSerializable));
    }

    /**
     * 反射破坏枚举单例
     */
    private static void enumReflectionTest() {
        try {
            Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
            declaredConstructor.setAccessible(Boolean.TRUE);
            EnumSingleton enumSingleton = declaredConstructor.newInstance("INSTANCE", 0);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 反射破坏单例
     */
    private static void reflectionTest() {
        try {
            Constructor<InnerClassSingletonSerializable> constructor = InnerClassSingletonSerializable.class.getDeclaredConstructor();
            constructor.setAccessible(Boolean.TRUE);
            InnerClassSingletonSerializable innerClassSingleton = constructor.newInstance();
            InnerClassSingletonSerializable innerClassSingleton1 = InnerClassSingletonSerializable.getInnerClassSingleton();
            System.out.println(innerClassSingleton == innerClassSingleton1);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 线程安全问题测试
     */
    private static void threadTest() {
        new Thread(() -> {
            LazySingleton singleton = LazySingleton.getSingleton();
            System.out.println(singleton);
        }).start();
        new Thread(() -> {
            LazySingleton singleton = LazySingleton.getSingleton();
            System.out.println(singleton);
        }).start();
    }

}

/**
 * 饿汉式
 * 借助jvm类加载机制，保证其唯一性
 */
class HungrySingleton {
    private static HungrySingleton hungrySingleton = new HungrySingleton();

    public static HungrySingleton getHungrySingleton() {
        return hungrySingleton;
    }

    private HungrySingleton() {
    }
}

/**
 * 懒汉式
 */
class LazySingleton {
    private static volatile LazySingleton lazySingleton;

    /**
     * 线程安全
     *
     * @return 单例
     */
    public static synchronized LazySingleton getSingleton() {
        if (null == lazySingleton) {
            lazySingleton = new LazySingleton();
        }
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return lazySingleton;
    }

    /**
     * 线程安全 且性能优异
     *
     * @return 单例
     */
    public static LazySingleton getSingletonOptimization() {
        if (null == lazySingleton) {
            synchronized (LazySingleton.class) {
                if (null == lazySingleton) {
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }


    private LazySingleton() {
    }
}

/**
 * 静态内部类
 * 懒加载(静态)
 * 线程安全
 */
class InnerClassSingleton implements Serializable {
    private static class InnerClassSingletonHolder {
        private static InnerClassSingleton innerClassSingleton = new InnerClassSingleton();
    }

    private InnerClassSingleton() {
        /* *//**
         * 安全校验
         *//*
        if (null != InnerClassSingletonHolder.innerClassSingleton) {
            throw new RuntimeException("单例不允许创建多个");
        }*/
    }

    public static InnerClassSingleton getInnerClassSingleton() {
        return InnerClassSingletonHolder.innerClassSingleton;
    }
}

/**
 * 反序列化
 */
class InnerClassSingletonSerializable implements Serializable {
    static final long serialVersionUID = 42L;

    private static class InnerClassSingletonHolder {
        private static InnerClassSingletonSerializable innerClassSingleton = new InnerClassSingletonSerializable();
    }

    private InnerClassSingletonSerializable() {
    }

    public static InnerClassSingletonSerializable getInnerClassSingleton() {
        return InnerClassSingletonHolder.innerClassSingleton;
    }

    /**
     * 保证 反序列化后 单例
     *
     * @return
     * @throws ObjectStreamException
     */
    Object readResolve() throws ObjectStreamException {
        return InnerClassSingletonHolder.innerClassSingleton;
    }
}

/**
 * 枚举实现 线程安全 反射安全 序列化安全
 */
enum EnumSingleton {
    INSTANCE;
    private String name;

    public void print() {
        System.out.println(INSTANCE.hashCode());
    }
}
